/*     */ package com.zimbra.cs.redolog.util;
/*     */ 
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.CliUtil;
/*     */ import com.zimbra.cs.redolog.RolloverManager;
/*     */ import com.zimbra.cs.redolog.logger.FileHeader;
/*     */ import com.zimbra.cs.redolog.logger.FileLogReader;
/*     */ import com.zimbra.cs.redolog.op.RedoableOp;
/*     */ import com.zimbra.cs.redolog.op.StoreIncomingBlob;
/*     */ import java.io.File;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.io.PrintStream;
/*     */ import java.io.RandomAccessFile;
/*     */ import java.text.SimpleDateFormat;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Date;
/*     */ import java.util.HashSet;
/*     */ import java.util.List;
/*     */ import java.util.Set;
/*     */ import org.apache.commons.cli.CommandLine;
/*     */ import org.apache.commons.cli.CommandLineParser;
/*     */ import org.apache.commons.cli.GnuParser;
/*     */ import org.apache.commons.cli.HelpFormatter;
/*     */ import org.apache.commons.cli.Options;
/*     */ import org.apache.commons.cli.ParseException;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class RedoLogVerify
/*     */ {
/*  58 */   private static Options sOptions = new Options();
/*     */   
/*     */   private static final String OPT_HELP = "h";
/*     */   private static final String OPT_QUIET = "q";
/*     */   private static final String OPT_SHOW_BLOB = "show-blob";
/*     */   private static final String OPT_NO_OFFSET = "no-offset";
/*     */   
/*     */   static
/*     */   {
/*  67 */     sOptions.addOption("h", "help", false, "show this output");
/*  68 */     sOptions.addOption("q", "quiet", false, "quiet mode.  Only print the log filename and any errors.  This option can be used to verify the integrity of redologs with minimal output.");
/*     */     
/*     */ 
/*  71 */     sOptions.addOption(null, "no-offset", false, "don't show file offsets and size for each redo op");
/*  72 */     sOptions.addOption(null, "m", true, "one or more mailbox ids separated by comma or white space.  The entire list must be quoted if using space as separator.  If this option is given, only redo ops for the specified mailboxes are dumped.  Omit this option to dump redo ops for all mailboxes.");
/*     */     
/*     */ 
/*     */ 
/*  76 */     sOptions.addOption(null, "show-blob", false, "show blob content.  Item's blob is printed, surrounded by <START OF BLOB> and <END OF BLOB> markers.  The last newline before end marker is not part of the blob.");
/*     */   }
/*     */   
/*     */ 
/*     */   private static void usage(String errmsg)
/*     */   {
/*  82 */     if (errmsg != null) {
/*  83 */       System.err.println(errmsg);
/*  84 */       System.err.println();
/*     */     }
/*  86 */     HelpFormatter formatter = new HelpFormatter();
/*  87 */     formatter.printHelp("zmredodump [options] <redolog file/directory> [...]", "where [options] are:\n", sOptions, "\nMultiple log files/directories can be specified.  For each directory, all redolog files directly under it are processed, sorted in ascending redolog sequence order.");
/*     */     
/*     */ 
/*     */ 
/*  91 */     System.exit(errmsg == null ? 0 : 1);
/*     */   }
/*     */   
/*     */   private static CommandLine parseArgs(String[] args) {
/*  95 */     CommandLineParser parser = new GnuParser();
/*  96 */     CommandLine cl = null;
/*     */     try {
/*  98 */       cl = parser.parse(sOptions, args);
/*     */     } catch (ParseException pe) {
/* 100 */       usage(pe.getMessage());
/*     */     }
/* 102 */     return cl;
/*     */   }
/*     */   
/*     */   private static class Params {
/* 106 */     public Set<Integer> mboxIds = new HashSet();
/* 107 */     public boolean quiet = false;
/* 108 */     public boolean hideOffset = false;
/* 109 */     public boolean showBlob = false;
/* 110 */     public boolean help = false;
/*     */   }
/*     */   
/*     */   private static Params initParams(CommandLine cl) {
/* 114 */     Params params = new Params(null);
/* 115 */     params.help = cl.hasOption("h");
/* 116 */     if (params.help) {
/* 117 */       return params;
/*     */     }
/* 119 */     params.quiet = cl.hasOption("q");
/* 120 */     params.hideOffset = cl.hasOption("no-offset");
/* 121 */     params.showBlob = cl.hasOption("show-blob");
/*     */     
/* 123 */     String mboxIdList = cl.getOptionValue("m");
/* 124 */     if (mboxIdList != null) {
/* 125 */       String[] ids = mboxIdList.split("[, ]+");
/* 126 */       if ((ids != null) && (ids.length > 0)) {
/* 127 */         for (String val : ids) {
/* 128 */           if ((val != null) && (val.length() > 0)) {
/*     */             try {
/* 130 */               int i = Integer.parseInt(val);
/* 131 */               if (i > 0) {
/* 132 */                 params.mboxIds.add(Integer.valueOf(i));
/*     */               } else
/* 134 */                 usage("Invalid mailbox id \"" + val + "\"");
/*     */             } catch (NumberFormatException e) {
/* 136 */               usage("Invalid mailbox id \"" + val + "\"");
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     
/* 143 */     return params;
/*     */   }
/*     */   
/*     */   private static class BadFile {
/*     */     public File file;
/*     */     public Throwable error;
/*     */     
/* 150 */     public BadFile(File f, Throwable e) { this.file = f;this.error = e;
/*     */     }
/*     */   }
/*     */   
/*     */   private static final String OPT_MAILBOX_IDS = "m";
/*     */   private PrintStream mOut;
/*     */   private Params mParams;
/*     */   private List<BadFile> mBadFiles;
/* 158 */   public RedoLogVerify(Params params, PrintStream out) { this.mOut = out;
/* 159 */     this.mParams = params;
/* 160 */     if (this.mParams == null)
/* 161 */       this.mParams = new Params(null);
/* 162 */     this.mBadFiles = new ArrayList();
/*     */   }
/*     */   
/*     */   public boolean scanLog(File logfile) throws IOException {
/* 166 */     boolean good = false;
/* 167 */     FileLogReader logReader = new FileLogReader(logfile, false);
/* 168 */     logReader.open();
/* 169 */     if (!this.mParams.quiet) {
/* 170 */       FileHeader header = logReader.getHeader();
/* 171 */       this.mOut.println("HEADER");
/* 172 */       this.mOut.println("------");
/* 173 */       this.mOut.print(header);
/* 174 */       this.mOut.println("------");
/*     */     }
/*     */     
/* 177 */     boolean hasMailboxIdsFilter = !this.mParams.mboxIds.isEmpty();
/*     */     
/* 179 */     RedoableOp op = null;
/* 180 */     long lastPosition = 0L;
/* 181 */     long lastOpStartOffset = 0L;
/*     */     try {
/* 183 */       while ((op = logReader.getNextOp()) != null) {
/* 184 */         lastOpStartOffset = logReader.getLastOpStartOffset();
/* 185 */         lastPosition = logReader.position();
/* 186 */         if (hasMailboxIdsFilter) {
/* 187 */           int mboxId = op.getMailboxId();
/* 188 */           if ((op instanceof StoreIncomingBlob)) {
/* 189 */             List<Integer> list = ((StoreIncomingBlob)op).getMailboxIdList();
/* 190 */             if (list != null) {
/* 191 */               boolean match = false;
/* 192 */               for (Integer mid : list) {
/* 193 */                 if (this.mParams.mboxIds.contains(mid)) {
/* 194 */                   match = true;
/* 195 */                   break;
/*     */                 }
/*     */               }
/* 198 */               if (!match) {
/*     */                 continue;
/*     */               }
/*     */             }
/*     */           } else {
/* 203 */             if (!this.mParams.mboxIds.contains(Integer.valueOf(mboxId)))
/*     */               continue;
/*     */           }
/*     */         }
/* 207 */         if (!this.mParams.quiet) {
/* 208 */           printOp(this.mOut, op, this.mParams.hideOffset, lastOpStartOffset, lastPosition - lastOpStartOffset);
/* 209 */           if (this.mParams.showBlob) {
/* 210 */             InputStream dataStream = op.getAdditionalDataStream();
/* 211 */             if (dataStream != null) {
/* 212 */               this.mOut.println("<START OF BLOB>");
/* 213 */               ByteUtil.copy(dataStream, true, this.mOut, false);
/* 214 */               this.mOut.println();
/* 215 */               this.mOut.println("<END OF BLOB>");
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/* 220 */       good = true;
/*     */ 
/*     */     }
/*     */     catch (IOException e)
/*     */     {
/* 225 */       this.mOut.println();
/* 226 */       this.mOut.printf("Error while parsing data starting at offset 0x%08x", new Object[] { Long.valueOf(lastPosition) });
/* 227 */       this.mOut.println();
/* 228 */       long size = logReader.getSize();
/* 229 */       long diff = size - lastPosition;
/* 230 */       this.mOut.printf("%d bytes remaining in the file", new Object[] { Long.valueOf(diff) });
/* 231 */       this.mOut.println();
/* 232 */       this.mOut.println();
/* 233 */       if (op != null) {
/* 234 */         this.mOut.println("Last suceessfully parsed redo op:");
/* 235 */         printOp(this.mOut, op, false, lastOpStartOffset, lastPosition - lastOpStartOffset);
/* 236 */         this.mOut.println();
/*     */       }
/*     */       
/*     */ 
/* 240 */       int bytesPerLine = 16;
/* 241 */       int linesBefore = 10;
/* 242 */       int linesAfter = 10;
/* 243 */       long startPos = Math.max(lastPosition - lastPosition % bytesPerLine - linesBefore * bytesPerLine, 0L);
/* 244 */       int count = (int)Math.min((linesBefore + linesAfter + 1) * bytesPerLine, lastPosition - startPos + diff);
/* 245 */       RandomAccessFile raf = null;
/*     */       try {
/* 247 */         raf = new RandomAccessFile(logfile, "r");
/* 248 */         raf.seek(startPos);
/* 249 */         byte[] buf = new byte[count];
/* 250 */         raf.read(buf, 0, count);
/* 251 */         this.mOut.printf("Data near error offset %08x:", new Object[] { Long.valueOf(lastPosition) });
/* 252 */         this.mOut.println();
/* 253 */         hexdump(this.mOut, buf, 0, count, startPos, lastPosition);
/* 254 */         this.mOut.println();
/*     */       } catch (IOException eh) {
/* 256 */         this.mOut.println("Error opening log file " + logfile.getAbsolutePath() + " for hexdump");
/* 257 */         eh.printStackTrace(this.mOut);
/*     */       } finally {
/* 259 */         if (raf == null) {}
/*     */       }
/*     */       
/*     */ 
/*     */ 
/* 264 */       throw e;
/*     */     } finally {
/* 266 */       logReader.close();
/*     */     }
/* 268 */     return good;
/*     */   }
/*     */   
/* 271 */   private static SimpleDateFormat sDateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS z");
/*     */   
/*     */   private static void printOp(PrintStream out, RedoableOp op, boolean hideOffset, long beginOffset, long size) {
/* 274 */     if (!hideOffset) {
/* 275 */       out.printf("[%08x - %08x: %d bytes; tstamp: %s] ", new Object[] { Long.valueOf(beginOffset), Long.valueOf(beginOffset + size - 1L), Long.valueOf(size), sDateFormatter.format(new Date(op.getTimestamp())) });
/*     */     }
/* 277 */     out.println(op.toString());
/*     */   }
/*     */   
/*     */   public boolean verifyFile(File file) {
/* 281 */     this.mOut.println("VERIFYING: " + file.getAbsolutePath());
/* 282 */     boolean good = false;
/*     */     try {
/* 284 */       good = scanLog(file);
/*     */     } catch (IOException e) {
/* 286 */       this.mBadFiles.add(new BadFile(file, e));
/* 287 */       this.mOut.println("Exception while verifying " + file.getAbsolutePath());
/* 288 */       e.printStackTrace(this.mOut);
/*     */     }
/* 290 */     if (!this.mParams.quiet)
/* 291 */       this.mOut.println();
/* 292 */     return good;
/*     */   }
/*     */   
/*     */   private boolean verifyFiles(File[] files) {
/* 296 */     boolean allGood = true;
/* 297 */     for (File log : files) {
/* 298 */       boolean b = verifyFile(log);
/* 299 */       allGood = (allGood) && (b);
/*     */     }
/* 301 */     return allGood;
/*     */   }
/*     */   
/*     */   private boolean verifyDirectory(File dir) {
/* 305 */     if (!this.mParams.quiet)
/* 306 */       this.mOut.println("VERIFYING DIRECTORY: " + dir.getAbsolutePath());
/* 307 */     File[] all = dir.listFiles();
/* 308 */     if ((all == null) || (all.length == 0)) {
/* 309 */       return true;
/*     */     }
/* 311 */     List<File> fileList = new ArrayList(all.length);
/* 312 */     for (File f : all) {
/* 313 */       if (!f.isDirectory()) {
/* 314 */         String fname = f.getName();
/* 315 */         if (fname.lastIndexOf(".log") == fname.length() - 4) {
/* 316 */           fileList.add(f);
/*     */         }
/*     */       }
/*     */     }
/* 320 */     File[] files = new File[fileList.size()];
/* 321 */     fileList.toArray(files);
/* 322 */     RolloverManager.sortArchiveLogFiles(files);
/* 323 */     return verifyFiles(files);
/*     */   }
/*     */   
/*     */   private void listErrors() {
/* 327 */     if (this.mBadFiles.size() == 0)
/* 328 */       return;
/* 329 */     this.mOut.println();
/* 330 */     this.mOut.println();
/* 331 */     this.mOut.println("-----------------------------------------------");
/* 332 */     this.mOut.println();
/* 333 */     this.mOut.println("The following files had errors:");
/* 334 */     this.mOut.println();
/* 335 */     for (BadFile bf : this.mBadFiles) {
/* 336 */       this.mOut.println(bf.file.getAbsolutePath());
/* 337 */       this.mOut.println("    " + bf.error.getMessage());
/*     */     }
/*     */   }
/*     */   
/*     */   private static void hexdump(PrintStream out, byte[] data, int offset, int length, long offsetOffsetBy, long badBytePos) {
/* 342 */     int end = Math.min(offset + length, data.length);
/* 343 */     int bytesPerLine = 16;
/* 344 */     while (offset < end) {
/* 345 */       int bytes = Math.min(bytesPerLine, end - offset);
/* 346 */       long offsetLineStart = offset + offsetOffsetBy;
/* 347 */       long offsetLineEnd = offsetLineStart + bytes;
/* 348 */       out.printf("%08x: ", new Object[] { Long.valueOf(offsetLineStart) });
/* 349 */       for (int i = 0; i < bytesPerLine; i++) {
/* 350 */         if (i < bytes) {
/* 351 */           out.printf("%02x", new Object[] { Integer.valueOf(data[(offset + i)] & 0xFF) });
/*     */         } else
/* 353 */           out.print("  ");
/* 354 */         out.print(" ");
/* 355 */         if (i == 7)
/* 356 */           out.print(" ");
/*     */       }
/* 358 */       out.print(" ");
/* 359 */       for (int i = 0; i < bytesPerLine; i++) {
/* 360 */         if (i < bytes) {
/* 361 */           int ch = data[(offset + i)] & 0xFF;
/* 362 */           if ((ch >= 33) && (ch <= 126)) {
/* 363 */             out.printf("%c", new Object[] { Character.valueOf((char)ch) });
/*     */           } else
/* 365 */             out.print(".");
/*     */         } else {
/* 367 */           out.print(" ");
/*     */         }
/*     */       }
/*     */       
/* 371 */       if ((offsetLineStart <= badBytePos) && (badBytePos < offsetLineEnd))
/* 372 */         out.print(" **");
/* 373 */       out.println();
/*     */       
/* 375 */       offset += bytes;
/*     */     }
/*     */   }
/*     */   
/*     */   public static void main(String[] cmdlineargs) {
/* 380 */     CliUtil.toolSetup();
/* 381 */     CommandLine cl = parseArgs(cmdlineargs);
/* 382 */     Params params = initParams(cl);
/* 383 */     if (params.help) {
/* 384 */       usage(null);
/*     */     }
/* 386 */     String[] args = cl.getArgs();
/*     */     
/* 388 */     if (args.length < 1) {
/* 389 */       usage("No redolog file/directory list specified");
/*     */     }
/* 391 */     boolean allGood = true;
/* 392 */     RedoLogVerify verify = new RedoLogVerify(params, System.out);
/*     */     
/* 394 */     for (int i = 0; i < args.length; i++) {
/* 395 */       File f = new File(args[i]);
/* 396 */       boolean good = false;
/* 397 */       if (f.isDirectory()) {
/* 398 */         good = verify.verifyDirectory(f);
/*     */       } else
/* 400 */         good = verify.verifyFile(f);
/* 401 */       allGood = (allGood) && (good);
/*     */     }
/*     */     
/* 404 */     if (!allGood) {
/* 405 */       verify.listErrors();
/* 406 */       System.exit(1);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/redolog/util/RedoLogVerify.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */